"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Version control systems are systems to **manage changes** to documents, computer programs, large web sites, and other collections of information."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## The main idea of version control systems\n",
"
\n",
" \n",
"
\n",
"\n",
"\n",
"* A version controlled system (typically) contains **one official repository**.\n",
"* Contributors work on **copies** of repository files and upload the changes to the official repository.\n",
"* **Conflicts** might occur if two people work on the same file simultaneously.\n",
" * Non-conflicting modifications are merged automatically.\n",
" * Conflicting modifications must be resolved manually. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Use cases for version control systems\n",
"\n",
"**Organization**\n",
" * Retrieve old versions of files.\n",
" * Print history of changes.\n",
" \n",
"**Collaboration**\n",
" * Share code between people and work simultanously on the same codebase\n",
" * Track changes and quickly undo changes if necessary\n",
"\n",
"**Backups**\n",
" * Store copy of git repository on an external platform e.g. github "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Git: the current standard for version control\n",
"\n",
" * git is a **fast**, **desentralized**, and **open-source** version control system\n",
" * Many sites for storing git repositories online (e.g. GitHub and Bitbucket).\n",
" * Installation instructions: . On Debian derivates (e.g. Ubuntu):\n",
" ```bash\n",
" sudo apt-get install git\n",
" ```\n",
" * Recommended book Pro Git (free to download [here](https://git-scm.com/book/en/v2))\n",
"
\n",
" \n",
"
\n",
" (The rest of the lecture uses material from this book)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
"# (clear everything before we start)\n",
"rm -rf ~/in3110/mysrc"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Creating your first git repository\n",
"\n",
"* A git repository is a folder in which files can be tracked by git. \n",
"* A git repository is created with:"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Initialized empty Git repository in /Users/minrk/in3110/mysrc/.git/\n"
]
}
],
"source": [
"mkdir -p ~/in3110/mysrc\n",
"cd ~/in3110/mysrc\n",
"git init . # The src folder is now also a git repository"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Git created a (hidden) directory `.git` in that folder which will contain all history information."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Adding files to the repository\n",
"* By default, git does not track any files.\n",
"* Files need to be **added** to the repository in order to track their changes:"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"myfile.py\n"
]
}
],
"source": [
"echo \"print(\\\"Hello\\\")\" > myfile.py # Create a new file myfile.py \n",
"git add myfile.py\n",
"ls"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Create a snapshot of the repository by **committing** the added file:"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[main (root-commit) 9e34d87] Initial version of myfile.py\n",
" 1 file changed, 1 insertion(+)\n",
" create mode 100644 myfile.py\n"
]
}
],
"source": [
"git commit -m 'Initial version of myfile.py'"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## The lifecycle of the status of your files \n",
"\n",
"* Files in your repository can either be **tracked** or **untracked**.\n",
"* Untracked files are always left untouched by `git`.\n",
"* Tracked files can be \n",
" * **unmodified** (no changes since last commit)\n",
" * **modified** (changes since last commit)\n",
" * **staged** (changes are ready to commit)\n",
"* This figure shows the full lifecycle: \n",
"
\n",
" \n",
"
\n",
"* The `git status` command shows in which status files are.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Inpsecting the changes made since the last commit\n",
"\n",
"Let's first make some changes"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [],
"source": [
"echo \"print(\\\"World\\\")\" >> myfile.py \n",
"echo \"This is a simple hello world project.\" > README.md"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We use `git status` to see the current state of the repo:"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"On branch main\n",
"Changes not staged for commit:\n",
" (use \"git add ...\" to update what will be committed)\n",
" (use \"git restore ...\" to discard changes in working directory)\n",
"\t\u001b[31mmodified: myfile.py\u001b[m\n",
"\n",
"Untracked files:\n",
" (use \"git add ...\" to include in what will be committed)\n",
"\t\u001b[31mREADME.md\u001b[m\n",
"\n",
"no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
]
}
],
"source": [
"git status"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Line-by-line changes since the last commit can be displayed with `git diff`"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1mdiff --git a/myfile.py b/myfile.py\u001b[m\n",
"\u001b[1mindex 2f9a147..3d3d93b 100644\u001b[m\n",
"\u001b[1m--- a/myfile.py\u001b[m\n",
"\u001b[1m+++ b/myfile.py\u001b[m\n",
"\u001b[36m@@ -1 +1,2 @@\u001b[m\n",
" print(\"Hello\")\u001b[m\n",
"\u001b[32m+\u001b[m\u001b[32mprint(\"World\")\u001b[m\n"
]
}
],
"source": [
"git diff"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Creating another commit\n",
"Let's stage all changes with `git add`: "
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [],
"source": [
"git add README.md myfile.py"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"On branch main\n",
"Changes to be committed:\n",
" (use \"git restore --staged ...\" to unstage)\n",
"\t\u001b[32mnew file: README.md\u001b[m\n",
"\t\u001b[32mmodified: myfile.py\u001b[m\n",
"\n"
]
}
],
"source": [
"git status"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If we are satisfied, we create a snapshot of the repo with `git commit`: "
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[main fe6fe87] New README.md file and fix in myfile.py\n",
" 2 files changed, 2 insertions(+)\n",
" create mode 100644 README.md\n"
]
}
],
"source": [
"git commit -m 'New README.md file and fix in myfile.py'"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Viewing the history of commits\n",
"* For every commit, git creates a snapshot of all tracked files in the repository.\n",
"* Each commit is identified by unique hash key\n",
"
\n",
" \n",
"
\n",
"\n",
"* `git log` can be used to view the commits in a repository:"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33mcommit fe6fe87b35c5692371df567a0bad56f259996213\u001b[m\u001b[33m (\u001b[m\u001b[1;36mHEAD -> \u001b[m\u001b[1;32mmain\u001b[m\u001b[33m)\u001b[m\n",
"Author: Min RK \n",
"Date: Wed Aug 23 13:19:07 2023 +0200\n",
"\n",
" New README.md file and fix in myfile.py\n",
"\n",
"\u001b[33mcommit 9e34d877368ad70d1da9235f9abc15bb27bd8fe7\u001b[m\n",
"Author: Min RK \n",
"Date: Wed Aug 23 13:15:32 2023 +0200\n",
"\n",
" Initial version of myfile.py\n"
]
}
],
"source": [
"git log"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"* Git allows us to view older version of the repository \n",
" * **But how do we know which version we are currently at?**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## The role of the `HEAD` pointer\n",
"\n",
"* `HEAD` is a special pointer that shows where you currently are in the repository history.\n",
"
\n",
" \n",
"
\n",
"* Running `git commit` updates the `HEAD` pointer to that latest commit.\n"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33mfe6fe87\u001b[m\u001b[33m (\u001b[m\u001b[1;36mHEAD -> \u001b[m\u001b[1;32mmain\u001b[m\u001b[33m)\u001b[m New README.md file and fix in myfile.py\n",
"\u001b[33m9e34d87\u001b[m Initial version of myfile.py\n"
]
}
],
"source": [
"git log --oneline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Some usefull command line arguments for `git log`:\n",
"* `--oneline`: summarize each commit as one line\n",
"* `git log FILENAME`: show commits affecting one file or directory"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Back to the future: Getting older revisions of a repository\n",
"\n",
"* To go to a previous snapshot of the repository:\n",
" * Simply **move the `HEAD` pointer to that commit**.\n",
" * All tracked files will automatically be updated to the version in that commit. \n",
"* The command for moving the `HEAD` pointer is `git checkout`:"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33mfe6fe87\u001b[m\u001b[33m (\u001b[m\u001b[1;36mHEAD -> \u001b[m\u001b[1;32mmain\u001b[m\u001b[33m)\u001b[m New README.md file and fix in myfile.py\n",
"\u001b[33m9e34d87\u001b[m Initial version of myfile.py\n"
]
}
],
"source": [
"git log --oneline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's revert to the first commit:"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Note: switching to 'main^1'.\n",
"\n",
"You are in 'detached HEAD' state. You can look around, make experimental\n",
"changes and commit them, and you can discard any commits you make in this\n",
"state without impacting any branches by switching back to a branch.\n",
"\n",
"If you want to create a new branch to retain commits you create, you may\n",
"do so (now or later) by using -c with the switch command. Example:\n",
"\n",
" git switch -c \n",
"\n",
"Or undo this operation with:\n",
"\n",
" git switch -\n",
"\n",
"Turn off this advice by setting config variable advice.detachedHead to false\n",
"\n",
"HEAD is now at 9e34d87 Initial version of myfile.py\n"
]
}
],
"source": [
"git checkout main^1"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {
"slideshow": {
"slide_type": "fragment"
},
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33mfe6fe87\u001b[m\u001b[33m (\u001b[m\u001b[1;32mmain\u001b[m\u001b[33m)\u001b[m New README.md file and fix in myfile.py\n",
"\u001b[33m9e34d87\u001b[m\u001b[33m (\u001b[m\u001b[1;36mHEAD\u001b[m\u001b[33m)\u001b[m Initial version of myfile.py\n"
]
}
],
"source": [
"git log --oneline --all"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `README.md` has disappeared and we have the initial version of `myfile.py` back:"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"myfile.py\n"
]
}
],
"source": [
"ls"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"print(\"Hello\")\n"
]
}
],
"source": [
"cat myfile.py"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"To move back to the latest version, we use:"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Previous HEAD position was 9e34d87 Initial version of myfile.py\n",
"Switched to branch 'main'\n"
]
}
],
"source": [
"git checkout main # alternatively use the identifier of the latest commit"
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"README.md myfile.py\n"
]
}
],
"source": [
"ls"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33mfe6fe87\u001b[m\u001b[33m (\u001b[m\u001b[1;36mHEAD -> \u001b[m\u001b[1;32mmain\u001b[m\u001b[33m)\u001b[m New README.md file and fix in myfile.py\n",
"\u001b[33m9e34d87\u001b[m Initial version of myfile.py\n"
]
}
],
"source": [
"git log --oneline --all"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Removing and moving files\n",
"\n",
"Files can be removed from the repository with\n",
"```bash\n",
"$ git rm myfile.py\n",
"```\n",
"\n",
"and moved with\n",
"\n",
"```bash\n",
"$ git mv myfile.py file.py\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## Tagging \n",
"\n",
"* Git has the ability to tag specific commits (i.e. give them a more memorable name than the identifier).\n",
"* Typically used to mark release points of your software"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## `git` cheat sheet (part 1)\n",
"\n",
" * `git init .`: create a new (local) repository\n",
"\n",
" * `git status`: View status of commited/uncommited files\n",
"\n",
" * `git commit -a`: create a commit of all tracked files\n",
"\n",
" * `git rm FILE`: remove a file\n",
"\n",
" * `git mv FILE`: move/rename a file\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"# Remote repositories\n",
"\n",
"We can work on git repositories that live on a remote location (for collaboration or backup).\n",
"\n",
"Let's say we created a git repository on github.com: https://github.com/minrk/mytest\n",
"\n",
"
\n",
"\n",
"
"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Working with remote repositories\n",
"\n",
"\n",
"Clone a remote repository to a local directory:\n",
"\n",
""
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"rm -rf ~/in3110/mytest"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Cloning into 'mytest'...\n",
"remote: Enumerating objects: 9, done.\u001b[K\n",
"remote: Counting objects: 100% (9/9), done.\u001b[K\n",
"remote: Compressing objects: 100% (5/5), done.\u001b[K\n",
"Receiving objects: 100% (9/9), done.\n",
"remote: Total 9 (delta 0), reused 6 (delta 0), pack-reused 0\u001b[K\n"
]
}
],
"source": [
"cd ~/in3110\n",
"git clone git@github.com:minrk/mytest.git mytest\n",
"cd mytest"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"README.md main.py\n"
]
}
],
"source": [
"ls"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Create a new commit and push it to the remote repository (requires write permission on the remote repository)."
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"print('Wed Aug 23 13:28:10 CEST 2023')\n",
"[main 4d6ce84] Add main.py file\n",
" 1 file changed, 1 insertion(+), 1 deletion(-)\n",
"README.md main.py\n"
]
}
],
"source": [
"echo \"print('$(date)')\" > main.py\n",
"cat main.py\n",
"git add main.py\n",
"git commit -m \"Add main.py file\"\n",
"ls"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33m4d6ce84\u001b[m\u001b[33m (\u001b[m\u001b[1;36mHEAD -> \u001b[m\u001b[1;32mmain\u001b[m\u001b[33m)\u001b[m Add main.py file\n",
"\u001b[33m093f8c0\u001b[m\u001b[33m (\u001b[m\u001b[1;31morigin/main\u001b[m\u001b[33m, \u001b[m\u001b[1;31morigin/HEAD\u001b[m\u001b[33m)\u001b[m Add main.py file\n",
"\u001b[33mc869fef\u001b[m Add main.py file\n",
"\u001b[33m1f174d7\u001b[m Create README.md\n"
]
}
],
"source": [
"git log --oneline"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Enumerating objects: 5, done.\n",
"Counting objects: 100% (5/5), done.\n",
"Delta compression using up to 10 threads\n",
"Compressing objects: 100% (2/2), done.\n",
"Writing objects: 100% (3/3), 977 bytes | 977.00 KiB/s, done.\n",
"Total 3 (delta 0), reused 0 (delta 0), pack-reused 0\n",
"To github.com:minrk/mytest.git\n",
" 093f8c0..4d6ce84 main -> main\n"
]
}
],
"source": [
"git push origin main"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33m4d6ce84\u001b[m\u001b[33m (\u001b[m\u001b[1;36mHEAD -> \u001b[m\u001b[1;32mmain\u001b[m\u001b[33m, \u001b[m\u001b[1;31morigin/main\u001b[m\u001b[33m, \u001b[m\u001b[1;31morigin/HEAD\u001b[m\u001b[33m)\u001b[m Add main.py file\n",
"\u001b[33m093f8c0\u001b[m Add main.py file\n",
"\u001b[33mc869fef\u001b[m Add main.py file\n",
"\u001b[33m1f174d7\u001b[m Create README.md\n"
]
}
],
"source": [
"git log --oneline"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"On https://github.com/minrk/mytest we can see the new commit has been uploaded.\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"You can download updates from remote repository with \n",
"\n",
"```bash\n",
"git pull origin main \n",
"```\n",
"\n",
"* Conflicting changes might have been made on the local and remote repository. \n",
"* This results merge conflicts which need to be resolved manually.\n",
"* This will be part of your first assignment."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## `git` cheat sheet (part 2)\n",
"\n",
" * `git clone URL`: clone a (remote) repository\n",
"\n",
" * `git pull origin main`: update file tree from (remote) repository\n",
"\n",
" * `git push origin main`: push changes to remote repository"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Branches\n",
"\n",
"\n",
"* Branches are lightweight copies of the main version \n",
"* Allow fast testing of new code without touching the default version\n",
"\n",
"
\n",
"\n",
"
\n",
"\n",
"* Remember: `HEAD` is a special pointer that shows where you currently are in the repository history.\n",
"* `main` (or sometimes `master`) is a default branch that is created when initializing a new repository."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Creating a branch"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Branches are created with the `git branch NAME` command."
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [],
"source": [
"cd ~/in3110/mysrc\n",
"git branch testing"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* The result is that we created a new pointer to the current commit.\n",
"* The `HEAD` pointer still points to the branch `main`.\n",
"
\n",
"\n",
"
"
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[33mfe6fe87\u001b[m\u001b[33m (\u001b[m\u001b[1;36mHEAD -> \u001b[m\u001b[1;32mmain\u001b[m\u001b[33m, \u001b[m\u001b[1;32mtesting\u001b[m\u001b[33m)\u001b[m New README.md file and fix in myfile.py\n",
"\u001b[33m9e34d87\u001b[m Initial version of myfile.py\n"
]
}
],
"source": [
"git log --oneline"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"# Switching to the new branch\n",
"\n",
"* We use the `git switch` command to move the `HEAD` pointer to our new branch"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Switched to branch 'testing'\n"
]
}
],
"source": [
"git switch testing"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"